{***************************************************************
    SpyGestion  Copyright (C) 2008 Christophe KOALI <nature2gas@gmail.com>
    
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
    
****************************************************************}
unit modFormulesDuJeu;

interface


uses modBaseType;

    // Formules conernant les flottes
    function mfdgGetDistance(Depart, Cible: TPlanetLocation): integer;
    function mfdgGetDurationTrajet(Distance, VitesseMax, VitesseRatio, SpeedFactor: integer): integer;
    function mfdgGetDurationTrajet100p(Depart, Cible: TPlanetLocation; Vitesse: integer; RatioSpeed: integer = 100; SpeedFactor: integer = 1): integer;

    function mfdgGetDurationTrajet100pSF(Depart, Cible: TPlanetLocation; Vitesse: integer; SpeedFactor: integer): integer;



    // Obtient la capacite de fret requise pour CAPTURER les ressources
    // d'une planète en l'attaquant
    // A partir de cette capacite de fret, on définit le nombre de vaisseuax à
    // envoyer
    // Version simple qui divise par deux la somme des ressources:
    function mfdgGetFretCapaciteRequise(Ressource: TOGRessourceAll): integer;

    // Version SpeedSim qui prend en compte la méthode de remplissage des vaisseaux
    function mfdgGetFretCapaciteRequiseContrainteSM(Ressource: TOGRessourceAll): integer;


    // A partir de la vitesse de base, et du niveau de la technologie combusion
    // la fonction retourne la vraie vitesse
    // ATTENTION: Le vaisseau doit utiliser la technologie combustion pour se déplacer
    function mfdgGetRealVitesseReacteurCombustion(Vitesse2Base: integer; NiveauCombustion: integer): integer;


    //
    // formules concernant les batiments
    //

    // Temps de construction pour un batiment donnée
    function mfdgGetTempsConstructionBatiment(RessourcesNecessaire: TOGRessourceAll; lvlUsineRobot, lvlNanite: integer): integer;

    // Production et consommation
    function mfdgGetProductionEtConsomation(gid: integer; Niveau: integer): TOGRessourceAll;

    // Temps de recherche
    // Attention: La prise en compte du laboratoire intergalactique doit se faire
    //            avant l'appel de cette fonction (et définir le bon niveau du labatoire)
    function mfdgGetTempsRecherche(RessourcesNecessaire: TOGRessourceAll; lvlLaboratoire: integer): integer;
    

implementation

uses Math, modGidToStr;


function mfdgGetRealVitesseReacteurCombustion(Vitesse2Base: integer; NiveauCombustion: integer): integer;
var doubleNiveau: double;
    doubleAjout : double;
begin
   // la technologie combustion n'augmente que de 10% la vitesse par niveau
   // 10% de la vitesse de base

   (*
avec la technologie armes niveau 1, il aura 50  + (1  * 10% * 50) = 55 d'attaque ;
avec la technologie armes niveau 2, il aura 50  + (2  * 10% * 50) = 60 d'attaque ;
...
avec la technologie armes niveau 10, il aura 50 + (10 * 10% * 50) = 100 d'attaque...' +
*)
    // 1 = 0.1
    // 2 = 0.2
    doubleNiveau := NiveauCombustion / 10.0;
    doubleAjout := doubleNiveau * Vitesse2Base;

    Result := Round(Vitesse2Base + doubleAjout);


end;






// Note: Cette fonction est "brut" et ne prend pas en compte "La recherche intergalactique"
//       La prise en compte de cela doit se faire avant l'appel de cette fonction
function mfdgGetTempsRecherche(RessourcesNecessaire: TOGRessourceAll; lvlLaboratoire: integer): integer;
var Ressources: double;
begin
(* http://board.ogame.fr/thread.php?threadid=442461
(métal + cristal) / (1000 * (1 + niveau labo + n meilleurs niveaux des labos autres que le labo de la planète effectuant la recherche))
Où n est le niveau du Réseau de recherche intergalactique et niveau labo est le niveau du laboratoire ou vous effectuez la recherche.

f_rtime := (metal, cristal, lvl) -> (metal + cristal) / (1000 * (1 + lvl));

*)

   Ressources := RessourcesNecessaire.Metal + RessourcesNecessaire.Cristal;

   Inc(lvlLaboratoire);
   
   // 1000 = 8 * 125
   lvlLaboratoire := lvlLaboratoire shl 3;
   lvlLaboratoire := lvlLaboratoire * 125;


   Ressources := Ressources /  lvlLaboratoire;

   Result := round(Ressources * 3600.0);


end;


function mfdgGetTempsConstructionBatiment(RessourcesNecessaire: TOGRessourceAll; lvlUsineRobot, lvlNanite: integer): integer;
var Ressources: double;
    dblRobot, dblNanite: double;
begin
   // [ (cristal + métal) / 5.000 ] * [ 2 / (1 + niveau Usine de Robot) ] * 0,5^niveau Nanites
   //
   // f_btime := (metal, cristal, robot, nanite) -> ((metal + cristal) / 5000) * (2 / (robot + 1)) * (0.5^nanite);

   if lvlUsineRobot < 0 then begin
      lvlUsineRobot := 0;
   end;
   if lvlNanite < 0 then begin
      lvlNanite := 0;
   end;

   Inc(lvlUsineRobot);
   dblRobot := lvlUsineRobot;


   Ressources := RessourcesNecessaire.Metal + RessourcesNecessaire.Cristal;
   Ressources := Ressources / (2500.0 * dblRobot);

   if lvlNanite > 0 then begin
      // 0.5; 0.5^2; 0.5^3;
      // 1/2,  1/4 , 1/8

      
      dblNanite := lvlNanite;
      
      dblRobot := Power(0.5, dblNanite);

      Ressources := Ressources * dblRobot;

   end;

   Result := Round(Ressources * 3600.0);


end;


function mfdgGetPrixElements(gid: integer; Niveau: integer): TOGRessourceAll;
var Factor: double;
    dbPow : double;
    dblNiveau: double;
begin

    Result.Metal     := 0;
    Result.Cristal   := 0;
    Result.Deuterium := 0;
    Result.Energie   := 0;

    if Niveau <= 0 then begin
       exit;
    end;

    // Implémenté dans modUnitAndDefInfos
    // directement dans la classe
    


    (*
    case gid of
        CNT_BATIMENT_METAL: begin

        end;
        CNT_BATIMENT_CRISTAL: begin

        end;

        CNT_BATIMENT_DEUT: begin

        end;

        CNT_BATIMENT_CENTRALE_SOLAIRE: begin

        end;

        CNT_BATIMENT_CENTRALE_FUSION: begin

        end;

        else begin
            Factor := 2.0;
        end;

    end;
    *)


end;

(*
// Prix: Prix au lvl 1
// Factor: Le facteur: 1,5^(n-1) ou 1,6^(n-1) ou 1,8^(n-1) ou 2^(n-1)  avec n le niveau
procedure mfdgGetPrixElementsFactor(var Prix: TOGRessourceAll; Factor: double);
begin
    Prix.Metal     := Round(Prix.Metal * Factor);
    Prix.Cristal   := Round(Prix.Cristal * Factor);
    Prix.Deuterium := Round(Prix.Deuterium * Factor);
    Prix.Energie   := Round(Prix.Energie * Factor);
end;
*)

function mfdgGetProductionEtConsomation(gid: integer; Niveau: integer): TOGRessourceAll;
const CNT_TEMP_MAX : double = 67.0;
var dbl11pow: double;
    dblNiveau: double;
begin

    Result.Metal     := 0;
    Result.Cristal   := 0;
    Result.Deuterium := 0;
    Result.Energie   := 0;

    if Niveau <= 0 then begin
       exit;
    end;

    dblNiveau := Niveau;
    dbl11pow  := Power(1.1, dblNiveau);

    // Source: http://board.ogame.fr/thread.php?threadid=442461


    case gid of
        CNT_BATIMENT_METAL: begin
(* Mine de métal :
    => Production = 30 * niveau * 1,1^niveau              f_metal := x -> 30 * x * (1.1^x);
    => énergie nécessaire = 10 * niveau * 1,1^niveau
*)
            dblNiveau := 30.0 * dblNiveau;
            dblNiveau := dblNiveau * dbl11pow;
            Result.Metal := Round(dblNiveau);

            dblNiveau := Niveau;
            dblNiveau := 10.0 * dblNiveau;
            dblNiveau := dblNiveau * dbl11pow;
            Result.Energie := -1 * Round(dblNiveau);


        end;
        

        CNT_BATIMENT_CRISTAL: begin
            dblNiveau := 20.0 * dblNiveau;
            dblNiveau := dblNiveau * dbl11pow;
            Result.Cristal := Round(dblNiveau);


            dblNiveau := Niveau;
            dblNiveau := 10.0 * dblNiveau;
            dblNiveau := dblNiveau * dbl11pow;
            Result.Energie := -1 * Round(dblNiveau);

        end;



        CNT_BATIMENT_DEUT: begin
(*      => Production = 10 * niveau * 1,1^niveau* (-0,002 * TempératureMax + 1,28
Maple: f_deut := (lvl, temp) -> 10 * lvl * (1.1^lvl) * (-0.002 * temp + 1.28);
        => énergie nécessaire = 20 * niveau * 1,1^niveau
*)

            // TempératureMax = 67

            dblNiveau := 10.0 * dblNiveau;
            dblNiveau := dblNiveau * dbl11pow;
            dblNiveau := dblNiveau * (-0.002 * CNT_TEMP_MAX + 1.28);
            Result.Deuterium := Round(dblNiveau);


            dblNiveau := Niveau;
            dblNiveau := 20.0 * dblNiveau;
            dblNiveau := dblNiveau * dbl11pow;
            Result.Energie := -1 * Round(dblNiveau);
        end;

        CNT_BATIMENT_CENTRALE_SOLAIRE: begin
(*  => Production = 20 * niveau * 1,1^niveau
*)

            dblNiveau := 20.0 * dblNiveau;
            dblNiveau := dblNiveau * dbl11pow;
            Result.Energie := Round(dblNiveau);
        end;

        
        CNT_BATIMENT_CENTRALE_FUSION: begin
(*  => Production = 50 * niveau * 1,1^niveau
    => Consommation de deutérium = 10 * niveau * 1,1^niveau
*)

            dblNiveau := 50.0 * dblNiveau;
            dblNiveau := dblNiveau * dbl11pow;
            Result.Energie := Round(dblNiveau);


            dblNiveau := Niveau;
            dblNiveau := 10.0 * dblNiveau;
            dblNiveau := dblNiveau * dbl11pow;
            Result.Deuterium := -1 * Round(dblNiveau);
        end;

        CNT_VAISSEAUX_SAT_SOLAIRE: begin
(* arrondi inférieur [ (TempératureMax / 4) + 20 ] (avec un maximum de 50 ou 51 d'énergie par satellite)
*)
            // Niveau correspond au nombre de satellites

            dbl11pow := CNT_TEMP_MAX / 4.0;
            dbl11pow := dbl11pow + 20.0;
            Result.Energie := Ceil(dblNiveau * dbl11pow);

        end;

        
        
    end;
end;


// TProcGetFretCapaciteRequise
function mfdgGetFretCapaciteRequiseContrainteSM(Ressource: TOGRessourceAll): integer;
var xMin, xTmp: integer;
    RessourceDiv2: TOGRessourceAll;
begin

(* m_Result.iNeededCapacity =
		MAX(th.met + th.kris + th.deut,
		    MIN((2 * th.met + th.kris + th.deut) * 3 / 4,
		        (2 * th.met + th.deut)
		    )
		);       *)
    RessourceDiv2.Metal     := Ressource.Metal     shr 1;
    RessourceDiv2.Cristal   := Ressource.Cristal   shr 1;
    RessourceDiv2.Deuterium := Ressource.Deuterium shr 1;


    xTmp := (Ressource.Metal + RessourceDiv2.Cristal + RessourceDiv2.Deuterium) * 3;
    xTmp := xTmp shr 2;

    xMin := min(xTmp,
                Ressource.Metal + RessourceDiv2.Deuterium);

    Result := max(RessourceDiv2.Metal + RessourceDiv2.Cristal + RessourceDiv2.Deuterium, xMin);
end;

// Version qui détermine le nombre de GT pour tout prendre
// en respectant les contraintes de remplissage
function mfdgGetFretCapaciteRequise(Ressource: TOGRessourceAll): integer;
var TotalRessource: integer;
begin
    TotalRessource := Ressource.Metal + Ressource.Cristal + Ressource.Deuterium;

    // pour l'instant, on utilise la formule de division par 2
    Result := TotalRessource shr 1;
end;








function mfdgGetDurationTrajet100pSF(Depart, Cible: TPlanetLocation; Vitesse: integer; SpeedFactor: integer): integer;
var Distance: integer;
begin

    if Vitesse <= 0 then begin
       Result := -1;
       exit;
    end;

    Distance := mfdgGetDistance(Depart, Cible);

    Result := mfdgGetDurationTrajet(Distance, Vitesse, 100, SpeedFactor);


end;

function mfdgGetDurationTrajet100p(Depart, Cible: TPlanetLocation; Vitesse: integer; RatioSpeed: integer = 100; SpeedFactor: integer = 1): integer;
var Distance: integer;
begin

    if Vitesse <= 0 then begin
       Result := -1;
       exit;
    end;

    Distance := mfdgGetDistance(Depart, Cible);

    Result := mfdgGetDurationTrajet(Distance, Vitesse, RatioSpeed, SpeedFactor);

end;

/////////////////////////////////////////////////////////////
// Distance : Distance à parcourir renvoyé par mbtGetDistance
// VitesseMax: Correspond à la vitesse de déplacement de la flotte, c'est à dire à la
//             vitesse du vaisseau le moins rapide
// VitesseRatio: On peut choisir le pourcentage de ralentissement, par tranche de 10%
//               100 est le maximum, 10 le minimum
function mfdgGetDurationTrajet(Distance, VitesseMax, VitesseRatio, SpeedFactor: integer): integer;
var DureeX1, DureeX2: Real;
begin

    if VitesseMax <= 0 then begin
       Result := -1;
       exit;
    end;

    if VitesseRatio < 0 then VitesseRatio  := 10;

    // Convertie le ratio pour l'adapter à la formule
    case VitesseRatio of
          0..14: VitesseRatio :=  1;
         15..24: VitesseRatio :=  2;
         25..34: VitesseRatio :=  3;
         35..44: VitesseRatio :=  4;
         45..54: VitesseRatio :=  5;
         55..64: VitesseRatio :=  6;
         65..74: VitesseRatio :=  7;
         75..84: VitesseRatio :=  8;
         85..94: VitesseRatio :=  9;
         else    VitesseRatio := 10;
    end;



         
(*
  msp = maxspeed();      // Vitesse de trajet (VitesseMax)
  sp = speed();          // Modifieur de vitesse (VitesseRatio)
  dist = distance();     // Distance à parcourir (Distance)

  return Math.round(((35000 / sp * Math.sqrt(dist * 10 / msp) + 10) / speedfactor));

  speedfactor est une variable caché qui à l'air d'être toujours 1 ...
  <input name="speedfactor" type="hidden" value="1" />


  Formule JavaScript avec nos variables:

  return Math.round(((35000 / VitesseRatio * Math.sqrt(Distance * 10 / VitesseMax) + 10) / speedfactor));

  *)

(*
  DureeX1 := sqrt(Distance * 10 / VitesseMax);
  DureeX1 := DureeX1 * VitesseRatio;
  DureeX1 := 35000.00 / DureeX1;
  DureeX1 := DureeX1 + 10.00;
*)

  // (35000 / VitesseRatio * DureeX1 + 10)

  
  DureeX1 := sqrt(Distance * 10 / VitesseMax);
  DureeX2 := 35000.00 / VitesseRatio;
  DureeX1 := DureeX1 * DureeX2;
  DureeX1 := DureeX1 + 10.00;


  // speedfactor est il toujours de 1 .
  if SpeedFactor > 1 then begin
     DureeX1 := DureeX1 / SpeedFactor;
  end;

  Result := Round(DureeX1);


  


end;


function mfdgGetDistance(Depart, Cible: TPlanetLocation): integer;
begin


    if Cible.Galaxie - Depart.Galaxie <> 0 then begin
       // Galaxie différente
       Result := Abs(Cible.Galaxie - Depart.Galaxie) * 20000;


    end else if Cible.SystemSolaire - Depart.SystemSolaire <> 0 then begin
       // Système solaire différent
       Result := Abs(Cible.SystemSolaire - Depart.SystemSolaire) * 5 * 19 + 2700;


    end else if Cible.Position - Depart.Position <> 0 then begin
       Result := Abs(Cible.Position - Depart.Position) * 5 + 1000;

    end else begin
       // même position (lune, champ de ruine)
       Result := 5;

    end;

(**
  thisGalaxy = document.getElementsByName("thisgalaxy")[0].value;
  thisSystem = document.getElementsByName("thissystem")[0].value;
  thisPlanet = document.getElementsByName("thisplanet")[0].value;

  targetGalaxy = document.getElementsByName("galaxy")[0].value;
  targetSystem = document.getElementsByName("system")[0].value;
  targetPlanet = document.getElementsByName("planet")[0].value;

  dist = 0;
  if ((targetGalaxy - thisGalaxy) != 0) {
    dist = Math.abs(targetGalaxy - thisGalaxy) * 20000;

  } else if ((targetSystem - thisSystem) != 0) {
    dist = Math.abs(targetSystem - thisSystem) * 5 * 19 + 2700;

  } else if ((targetPlanet - thisPlanet) != 0) {
    dist = Math.abs(targetPlanet - thisPlanet) * 5 + 1000;

  } else { 
    dist = 5;
  }

  return(dist);
  *)

end;


end.
